home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 4
/
Aminet 4 - November 1994.iso
/
aminet
/
util
/
boot
/
appvm.lha
/
AppVM
/
appvm.s
< prev
next >
Wrap
Text File
|
1994-01-28
|
54KB
|
1,668 lines
*************************************************************************************
*
* AppVM ® (Application VIRTUAL MEMORY) © Copyright LVA March 1992
* ------- --------------------------
*
* Written by Laurence Vanhelsuwé.
*
*
* AppVM ® is a stop-gap solution for application writers who need UNIX-style
* Demand Paged Virtual Memory.
* This program extends the functionality of Exec's AllocMem() and FreeMem() and
* AmigaDOS's Read() and Write().
* Only Processes can use VM, not Tasks !
*
* This implementation behaves like an exclusive resource like PAR: or SER: as
* opposed to a shared resource. A single Process at a time can allocate (and use)
* areas of memory that are larger than physical RAM memory as if that memory was
* physically present.
* As soon as this unique Process (referred to in the source as the "client") frees
* all its VM, another client can immediately start using the resource again.
*
* This program is called Application VM because the system (Exec, Intuition)
* is never allowed to handle VM pointers.
* Only applications are able to get VM and use it "internally", i.e. they are not
* allowed to pass a VM ptr to the system (via a call to DoIO() for example).
* (More precisely, bus error exceptions due to the dereferencing of VM ptrs are
* only handled correctly if the code trapping is in the scope of the client Process.)
*
* There's one important exception to this rule:
* Exec's AllocMem() is the source of VM ptrs and FreeMem(), Read() and Write() can
* all handle VM ptrs.
* This should be enough for any application (who gets the status of "client") to
* process huge amounts of data and load and/or save into/from VM.
*
* History
* -------
* 24/MAR/92: started code (goal: getting the Amiga to use my new MMU tree)
* 25/MAR/92: (goal: getting instructions which use VM to restart)
* 26/MAR/92: (goal: getting a client Process to use service)
* 27/MAR/92: (goal: full service incl. Read()/Write() )
* 29/APR/92: fixed double Close() bug in case disk full
*
*************************************************************************************
include std
include hardware/custom.i
include exec/vmemory.i ;include our new VM include!
;-----------------------------------------------
; Program Global Constants
;-----------------------------------------------
; This version allows a maximum VM request of 256Mb (if you've got that much spare
; on your hard disk). The code can handle more though. **!! (How much, 2 Gb ?)
; The absolute maximum in any case is 2 Gigabytes since VM addresses start at
; $80000000.
MAX_VM_MEGS equ 256 ;256Mb max LA range
; Since we're only using a single B-level table for the VM logical addresses (which
; have bit31=1), I can only map a maximum VM range of 256Mb.
; That should be enough since you also need that much space on your hard disk!
KILOBYTE equ 1024
MEGABYTE equ KILOBYTE*KILOBYTE
PAGE_SHIFT equ 15 ;2^15 = 32K
PAGE_SIZE equ 1<<PAGE_SHIFT ;one PAGE is 32K big
; Pages are this large because:
;
; 1) AmigaDOS is very efficient at transfering large chuncks from/to disk.
; 2) Bus exception errors become less frequent.
; 4) The D-level table shrinks significantly.
; 3) The free pages bitmap shrinks too.
VM_START equ $80000000 ;all VM addresses have b31=1
; What this program does is to create a new MMU table which CONTAINS the standard
; 2-level (A,B) Exec MMU table and a new branch for VM addresses.
; The standard table uses TIA=8 and TIB=6 with a table A index LIMIT of 16.
; This means that the standard table provides translation for the very first 256Mb
; of address space in the Amiga. Everything above it is off-limit (BUS ERROR).
; What AppVM does is to "shift down" this tree and turn it into a branch of a bigger
; tree using the following LA field subdivisions:
; TABLE ENTRIES PER ENTRY
; ----- ------- ---------
TIA equ 4 ; 4Gb 16 = 256 Mb
TIB equ 4 ;256Mb 16 = 16 Mb
TIC equ 6 ; 16Mb 64 = 256 K
TID equ 3 ;256K 8 = 32 K PAGES
; Check whether chosen logical address breakdown fields are legal.
TC_CHECK equ 32-(TIA+TIB+TIC+TID+PAGE_SHIFT)
IFNE TC_CHECK
blabla ; ** WARNING ** SELECTED LA INDEX BREAK-DOWN IS INVALID
END
ENDC
; The new TIB,TIC values have to be derived from the TIA,TIB values of the original
; translation tree. TIC has to be the old TIB. TIB Can be 4 instead the original
; TIA's 8 because the original A-table is LIMITED to the first 16 indices.
; This is necessary so that I can retain the existing MMU translation tree as a
; branch of my bigger tree (which is now a 4-level tree).
*####################################################################################
*
* RESULTING MMU TRANSLATION TREE STRUCTURE:
* =========================================
*
* A-table (one for ENTIRE SYSTEM)
*
* ____________
* 0 |_1st 256Mb|------> STANDARD Exec MMU table (becomes levels B and C)
* 1 | |
* 2 | |
* 3 | |
* 4 | |
* 5 | |
* 6 | |
* 7 | |
* 8 |_8th 256Mb|------> VM B-table
* 9 | | ____________
* A | | | 1st 16Mb |------> 1st VM C-table
* B | | | 2nd 16Mb | ______________
* C | | | 3rd 16Mb | | 1st 256K |-----> 1st VM D-table
* D | | | 4th 16Mb | | 2nd 256K | _______________
* E | | | 5th 16Mb | | | | 1st 32K PAGE |
* F | | | 6th 16Mb | : : | 2nd 32K PAGE |
* ------------ | 7th 16Mb | | 3rd 32K PAGE |
* All other ranges | 8th 16Mb | 64 entries | 4th 32K PAGE |
* are INVALID. | 9th 16Mb | | 5th 32K PAGE |
* |10th 16Mb | : : | 6th 32K PAGE |
* |11th 16Mb | | | | 7th 32K PAGE |
* |12th 16Mb | | 63rd 256K | | 8th 32K PAGE |
* |13th 16Mb | | 64th 256K | ----------------
* |14th 16Mb | --------------
* |15th 16Mb |
* |16th 16Mb |
* ------------
*####################################################################################
A_TSIZE equ (1<<TIA)*4 ;64 ;size of one table at each level
B_TSIZE equ (1<<TIB)*4 ;64 ;(using short descriptors)
C_TSIZE equ (1<<TIC)*4 ;256
D_TSIZE equ (1<<TID)*4 ;32
C_TABLES equ 1<<TIB ;number of C tables depends on B level
D_TABLES equ 1<<(TIB+TIC) ;1024 of them ! (8192 descriptors)
TREE_SIZE equ A_TSIZE+B_TSIZE+(C_TABLES*C_TSIZE)+(D_TABLES*D_TSIZE)
; 64 + 64 + 4096 + 32768
SHORT_INVALID equ $BAD00000 ;DT bits = 00 = INVALID
;this descriptor HAS to have b31=1 (quick test)
PTR_MASK equ $FFFFFF00 ;low descriptor byte is not part of ptr
MODIFIED_PAGE equ 4 ;bit set if page has been written to
ACCESSED_PAGE equ 3 ;bit set if page has been accessed (R/W)
DT_INVALID equ $0
DT_PAGE equ $1
DT_VALID_4BYTE equ $2 ;table pointer points to short table
DT_VALID_8BYTE equ $3 ;table pointer points to short table
TC_ENABLE equ 1<<31
TC_PSIZE equ (%1111)<<20 ;select 32K pages
TC_TIA equ TIA<<12
TC_TIB equ TIB<<8
TC_TIC equ TIC<<4
TC_TID equ TID
NEW_TC equ TC_ENABLE+TC_PSIZE+TC_TIA+TC_TIB+TC_TIC+TC_TID
_intena equ $dff000+intena ;for DISABLE/ENABLE
;##########################################################################
START_APPVM: move.l sp,stack_level ;store original sp
move.l a0,arg_line ;store raw arguments ptr
bsr init_all ;init vars, open DOS, stdout
beq bail_out ;if couldn't open something: exit now
move.l 4.w,a6 ;check to see that this machine is
move.w AttnFlags(a6),d0 ;at least 030 based coz I need MMU
btst #AFB_68030,d0 ;(**!! SIMPLISTIC TEST: tst mmu config!)
beq need_MMU
bsr parse_args ;get arguments (VM size, swapfile name)
cmp.l #$00FFFFFF,$8 ;has other copy of APPVM already been
bhi already_resident ;started ? (**!! SIMPLISTIC TEST)
bsr create_swapfile ;create swapfile on disk
bsr init_VM_pool ;VM logical address range management
beq no_VM_init
bsr install_patches ;modify system to handle VM accesses
lea VM_ready,a0 ;"Virtual Memory Manager 0.9 Ready"
bsr print_error
move.l 4.w,a6 ;using exec library
move.l #SIGBREAKF_CTRL_F,d0 ;Wait() until explicitly killed
EXEC Wait ;using BREAK PROCESS x F
; At this stage this Process falls asleep but its code can temporarily come to life
; when there are bus errors or when other Processes call AllocMem() or FreeMem().
bail_out bsr unpatch_all ;return Alloc/FreeMem to normal.
lea VM_off,a0 ;"Virtual Memory Manager no longer active"
bsr print_error
move.l 4.w,a6 ;using Exec
move.l vm_tree,d0 ;did we ever allocate the VM tree ?
beq no_tree
move.l d0,a1
move.l #TREE_SIZE,d0
EXEC FreeMem
no_tree move.l vm_bitmap,d0 ;dealloc pages bitmap
beq no_bitmap
move.l d0,a1
move.l bitmap_size,d0
EXEC FreeMem
no_bitmap bsr close_all ;used libraries, handles
END_APPVM: move.l stack_level,sp ;restore sp to original
rts
;##########################################################################
;-----------------------------------------------
; ERROR PRINTING ROUTINES
;-----------------------------------------------
no_table lea no_table_str,a0
bsr print_error
bra bail_out
;-----------------------------------------------
give_example lea give_example_str,a0
bsr print_error
bra bail_out
;-----------------------------------------------
too_much_vm lea too_much_vm_str,a0
bsr print_error
bra bail_out
;-----------------------------------------------
not_enough_vm lea not_enough_vm_str,a0
bsr print_error
bra bail_out
;-----------------------------------------------
no_VM_init lea no_VM_init_str,a0
bsr print_error
bra bail_out
;-----------------------------------------------
unable_to_create lea unable_to_create_str,a0
bsr print_error
bra bail_out
;-----------------------------------------------
disk_full lea disk_full_str,a0
bsr print_error
bra bail_out
;-----------------------------------------------
already_resident lea resident_str,a0
bsr print_error
bra bail_out
;-----------------------------------------------
need_MMU lea need_MMU_str,a0
bsr print_error
bra bail_out
;-----------------------------------------------
; Check user arguments.
;
; Template: VMMEGS/A,SWAPFILE/K
;-----------------------------------------------
parse_args move.l #default_swfn,swap_filename ;set default filename
move.l #APPVM_template,d1
move.l #appvm_results,d2
bsr ReadArgs ;parse args according to template
beq give_example
move.l vm_megs,a0 ;get pointer to REQUIRED VM size arg
bsr DEC_TO_BIN ;convert from string to binary
cmp.l #MAX_VM_MEGS,d1 ;reasonable size request ?
bhi too_much_vm
moveq #20,d0 ;turn into bytes (2^20 = 1Meg)
lsl.l d0,d1
move.l d1,vm_bytes ;remember size of VM to manage
beq not_enough_vm
rts
;-----------------------------------------------
; Create a "swap partition on hard disk".
; This is actually just any old AmigaDOS file on any device.
;-----------------------------------------------
create_swapfile move.l DOS_LIB_PTR,a6 ;using DOS..
move.l swap_filename,d1
move.l #MODE_OLDFILE,d2 ;assume file already exists..
DOS Open ;open for R/W accesses
move.l d1,swap_fhandle
bne set_size
move.l swap_filename,d1 ;if not then create fresh.
move.l #MODE_NEWFILE,d2
DOS Open
move.l d1,swap_fhandle
beq unable_to_create ;exit if that didn't work either!
;---------------
set_size move.l vm_bytes,d2 ;force existing file to be large
move.l #OFFSET_BEGINNING,d3 ;enough for our VM range
DOS SetFileSize ;(shrink or stretch)
cmp.l d2,d0
bne no_stretch ;did sizing work ?
rts
;-----------------------------------------------
; Couldn't create such a large file on disk.
; Close file and Delete it before reporting failure.
;-----------------------------------------------
no_stretch move.l swap_fhandle,d1 ;free lock on file
DOS Close
move.l swap_filename,d1
DOS DeleteFile ;remove it (otherwise disk 100% full)
clr.l swap_fhandle ;tell cleanup() we've done it.
bra disk_full
;-----------------------------------------------------------------------------------
; Allocate a fresh RAM page so that a new VM page can be enabled and loaded
; from disk.
; If no external RAM is available, then go through D-table descriptors and
; find any page which hasn't been modified yet and rob it to satisfy our need.
; If all pages have been modified, then take the first page and swap it out
; to disk (last resort).
;
; This routine is the USER mode part of the exception handler.
;
; naughty_vm_ptr -> VM address that caused the bus error exception
;-----------------------------------------------------------------------------------
add_VM_page bsr age_pages ;**!! should be in IRQ
move.l naughty_vm_ptr,d0
and.l #~(PAGE_SIZE-1),d0
move.l d0,naughty_vm_ptr ;calc aligned VM PAGE address
move.l #PAGE_SIZE,d7 ;get RAM aligned to a page boundary
move.l #PAGE_SIZE,d0 ;size of allocation
bsr alloc_aligned ;get fresh page
move.l d0,d7 ;store addr in parm for enable_page
beq running_low ;if succeeded,
;---------------
localize_page move.l naughty_vm_ptr,a2 ;-> VM addr at which
move.l d7,a3 ;this RAM page should appear.
bsr load_page ;fill page from copy on swapdevice
bsr enable_page ;and change MMU table to enable page
return_to_excep lea bus_error_tail,a5 ;-> continuation point of exc. handler
move.l 4.w,a6
EXEC Supervisor ;finish off and never come back here
; -- WE NEVER RETURN HERE --
************************************************************************************
;----------------------------------------------------------------------
; Can't allocate a page from RAM, so steal a page from MMU tree itself.
; There's always at least one mapped page in the tree. **!!
;----------------------------------------------------------------------
running_low move.l D_tables,a0 ;-> list of page descriptors
moveq #0,d0
bset #TIB+TIC+TID,d0 ;total # of descriptors in D-tables
find_unmodified move.l (a0)+,d7 ;get a page descriptor
bclr #0,d7 ;is this a valid page ?
beq bad_page ;yes,
move.l d7,d6 ;remember any valid page for worse case
move.l a0,d5 ;and remember associated VM address !
bclr #ACCESSED_PAGE,d7 ;is this an old page ?
bne bad_page ;yep,
bclr #MODIFIED_PAGE,d7 ;is this page still virgin ?
beq rob_page ;no,
bad_page sub.l #1,d0 ;more descriptors available ?
bne find_unmodified ;no, use last valid page regardless then
and.l #PTR_MASK,d6 ;isolate robbed dirty page address
move.l d5,d0 ;descr address in D-table (+4)
sub.l #4,d0 ;undo descriptor scan postincrement
sub.l D_tables,d0 ;= VM page # (*4)
moveq #PAGE_SHIFT-2,d1 ;(-2 because already multiplied by 4)
lsl.l d1,d0 ;VM byte offset
add.l #VM_START,d0 ;-> valid VM address
move.l d0,a2
move.l d6,a3 ;before robbing dirty page: save it to
bsr save_page ;swapdevice
move.l d6,d7 ;and now drop through to rob page.
move.l d5,a0 ;restore D-table ptr
;---------------
rob_page move.l #SHORT_INVALID,-(a0) ;grab page to enable it somewhere else
and.l #PTR_MASK,d7 ;isolate RAM page address
bra localize_page ;poof! you're used for other VM page
;-----------------------------------------------
; Map RAM page at VM address "naughty_vm_ptr"
;
; D7 -> PHYSICAL RAM PAGE
;-----------------------------------------------
enable_page or.w #DT_PAGE,d7 ;turn address into descriptor
move.l naughty_vm_ptr,d0
bclr #31,d0 ;change VM ptr into slot offset
moveq #PAGE_SHIFT,d1
lsr.l d1,d0 ;d1 = slot number
move.l D_tables,a0 ;change MMU table to map new page
move.l d7,(a0,d0.l*4) ;enable new page
rts
;-----------------------------------------------
; Age pages.
; Using a low priority clock interrupt, go clear the ACCESSED bits in a block
; of page descriptors. Do so in a circular buffer fashion.
;-----------------------------------------------
age_pages move.l D_tables,a0 ;-> base of D-level tables
move.l ager,d0 ;-> circular descr index
move.w #4096-1,d1 ;age N descriptors at a time **!!
move.l #(1<<(TIB+TIC+TID))-1,d2 ;circular buffer index mask
age_em bclr #ACCESSED_PAGE,3(a0,d0.l*4) ;to "age"
add.l #1,d0
and.l d2,d0 ;keep index inside circular buffer
dbra d1,age_em
move.l d0,ager
rts
;-----------------------------------------------
; Copy a page from VM to disk.
; A2 -> VM address
; A3 -> Source Machine address
;-----------------------------------------------
save_page move.l DOS_LIB_PTR,a6 ;using DOS...
move.l swap_fhandle,d1 ;seek to correct page in file
move.l a2,d2
bclr #31,d2 ;(turn VM ptr into file offset)
move.l #OFFSET_BEGINNING,d3
DOS Seek
move.l swap_fhandle,d1
move.l a3,d2 ;write page back to disk
move.l #PAGE_SIZE,d3
DOS Write
rts
;-----------------------------------------------
; Copy a page from disk to VM.
;
; A2 -> VM Address
; A3 -> Dest Machine address
;-----------------------------------------------
load_page movem.l d2/d3,-(sp) ;save non-scratch regs
move.l DOS_LIB_PTR,a6 ;using DOS...
move.l swap_fhandle,d1 ;seek to correct page in file
move.l a2,d2
bclr #31,d2 ;(turn VM ptr into file offset)
move.l #OFFSET_BEGINNING,d3
DOS Seek
move.l swap_fhandle,d1
move.l a3,d2 ;read page from disk
move.l #PAGE_SIZE,d3
DOS Read
movem.l (sp)+,d2/d3 ;restore non-scratch regs
rts
;-----------------------------------------------
; Using a block of memory aligned to a 16-byte boundary, initialize it to
; hold the COMPLETE translation tree for all VM addresses in the range
; of the requested VM size.
; This table will become a BRANCH off the A-level table to handle
; VM addresses only. VM Addresses all have bit 31 SET (e.g $80000000).
;-----------------------------------------------
gen_VM_tree moveq #16,d7 ;get RAM aligned to 16-byte boundary
move.l #TREE_SIZE,d0 ;A+B+C+D
bsr alloc_aligned
move.l d0,vm_tree ;store address of our future MMU table
beq no_table
;---------------
move.l d0,a0 ;-> available aligned RAM to fill
move.l d0,a1 ;copy
moveq #0,d0
bset #TIA,d0 ;build the one and only A-level table
gen_A_table move.l #SHORT_INVALID,(a0)+
sub.l #1,d0
bne gen_A_table
move.l a0,B_table ;remember ptr to VM B-table
move.l a0,d0
or.w #DT_VALID_4BYTE,d0
move.l d0,8*4(a1) ;let table A point to my VM tree
;---------------
lea B_TSIZE(a0),a1 ;-> first C-level table
move.l a1,d0
or.w #DT_VALID_4BYTE,d0
move.l d0,a1 ;descriptor to store in B table
moveq #0,d0
bset #TIB,d0 ;build the one and only B-level table
gen_B_table move.l a1,(a0)+ ;store table descriptors to the 16
add.l #C_TSIZE,a1 ;C-level tables
sub.l #1,d0
bne gen_B_table
;---------------
move.l a0,C_tables
moveq #0,d0
bset #TIB+TIC,d0 ;construct 16 C-level tables
gen_C_tables move.l a1,(a0)+ ;store descriptors to D-level tables
add.l #D_TSIZE,a1
sub.l #1,d0
bne gen_C_tables ; (64 entries per table) * 16 tables
;-------------------------------------
; The D-tables don't contain further table pointers
; but either PAGE descriptors or INVALID descriptors.
;-------------------------------------
move.l a0,D_tables ;remember where D-level tables are
moveq #0,d0
bset #TIB+TIC+TID,d0 ;construct 16*64 D-level tables
gen_D_tables move.l #SHORT_INVALID,(a0)+
sub.l #1,d0
bne gen_D_tables ; (8 entries per table)
rts
;-----------------------------------------------
; Modify MMU registers CRP and TC to make MMU use my new tree.
;-----------------------------------------------
use_new_tree move.l 4.w,a6 ;using exec library
;can't have any interrupts using untranslated ROM addresses !
DISABLE
lea new_MMU_table,a5 ;-> MMU surgery routine
EXEC Supervisor
ENABLE ;re-enable IRQs, multi-tasking etc..
rts
;-----------------------------------------------
; Read current MMU configuration and store values.
; Construct new MMU register values and poke them (enabling new tree).
;-----------------------------------------------
new_MMU_table move.l vm_tree,a0 ;-> A-table
lea old_crp_room,a1 ;-> room to store original CRP
lea new_crp_room,a2 ;-> room to construct new CRP
lea old_tc,a3 ;-> spot to save current TC in
pmove.d CRP,(a1) ;get current CRP
pmove.l TC,(a3) ;get current TC
move.l 4(a1),d0 ;get translation table address
or.w #DT_VALID_4BYTE,d0 ;turn into a valid table descriptor
move.l d0,(a0) ;store in new A-table
lea tc_long,a3
clr.l (a3)
pmove.l (a3),TC ;disable MMU translation
; The ATC just got flushed.
; At this point the next PC won't be translated by the MMU
; but will be used "raw" by the Amiga bus sub-system.
move.l #$7FFF<<16+DT_VALID_4BYTE,d0 ;no LIMIT for A-table index
move.l d0,(a2)
move.l a0,4(a2) ;construct new CRP
pmove.d (a2),CRP ;point MMU to our new tree
move.l #NEW_TC,(a3)
pmove.l (a3),TC ;re-enable MMU with new table
rte
**********************************************************************************
**********************************************************************************
* This is the core of the program: the 68030 BUS ERROR handler.
* It allows non-existent VM addresses to be used by trapping the generated
* bus errors and modifying the MMU tables and swapping pages in & out of
* physical RAM from/to disk.
**********************************************************************************
**********************************************************************************
REGS_BLOCK equ (8+8)*4 ;64 bytes for MOVEM.L block
;-----------------------------------------------
bus_err_handler tst.l client_Process ;if we haven't got a VM user...
beq normal_bus_err ;then use normal Exec handler
;---------------
movem.l a0/a1,-(sp) ;push some regs
move.l 4.w,a0 ;-> Exec library
move.l client_Process,a1 ;this Process MUST be our client...
cmp.l ThisTask(a0),a1
bne pop_n_normal ;if not let Task handle excep. itself
movem.l (sp)+,a0/a1 ;restore temp regs
;---------------
movem.l d0-d7/a0-a6,-(sp) ;don't touch ANY user regs!
move.l USP,a0
move.l a0,-(sp) ;note Task's user SP !
lea REGS_BLOCK(sp),a0 ;-> Bus error exception stack frame
move.l $10(a0),naughty_vm_ptr ;get VM ptr that caused exception
; lea $200,a1 ;-> area to dump stack frame in
; moveq #$60-1,d0
;copy_frame move.b (a0)+,(a1)+
; dbra d0,copy_frame
move.w #$F00,$dff180 ;show sign of exception occurring
move.w #$FFF,$dff180 ;show sign of exception occurring
move.w #$FFF,$dff180 ;show sign of exception occurring
move.w #$FFF,$dff180 ;show sign of exception occurring
move.w #$F00,$dff180 ;show sign of exception occurring
move.w #$AAA,$dff180
; Create an RTE frame that will make a Hyperjump to USER code to do all
; the neccessary page swapping (loading/saving) and then eventually
; switch back to SUPER mode to pop all original regs back and restart the
; broken instruction.
;note level of SSP before exiting. If SSP isn't the same when returning: PANIC !
move.l sp,continue_ssp
move.w #$0010,-(sp) ;TYPE+VEC ;Illegal Inst. frame
move.l #add_VM_page,-(sp) ;PC ;
move.w #$0000,-(sp) ;SR ;TT=S=0 and IL = 0 !!
rte
; At this point the CPU whizzes off to "add_VM_page" and will eventually
; return to finish off this exception at "bus_error_tail".
;-----------------------------------------------
pop_n_normal movem.l (sp)+,a0/a1 ;if not: restore regs, go to Exec
normal_bus_err jmp $F80000 ;**!! SELF-MODIFIED
;------------------------------------------------------------------------------------
; We're nearing the end of our BUS ERROR exception handler.
; The user code responsible for doing disk accesses has jumped to here via
; a Supervisor() call.
; That's just to get us back into supervisor mode, so discard Supervisor RTE
; frame.
;
; **!! Normally, when Tasks call the Supervisor() function they ALWAYS find the
; SSP pointing to the same spot. The entire VM system relies on this, therefore
; to avoid crashing due to another Task modifying the level of the supervisor stack
; we save and restore the SSP, this means that any other Task in the system which
; messes around with the supervisor stack will get hit by my SSP resets.
;------------------------------------------------------------------------------------
bus_error_tail move.l continue_ssp,a7 ;discard Supervisor() stack frame
;(in theory nothing more)
; At this point the supervisor stack is back in the state we
; left it in when switching from SUPER to USER mode with the RTE above.
move.l (sp)+,a0
move.l a0,USP ;restore user SP
movem.l (sp)+,d0-d7/a0-a6 ;restore regs
rte ;restart instruction !
;-----------------------------------------------
; Allocate and initialize the bitmap which represents the VM pages pool.
; The VM Pool represents allocated or free VM logical address ranges, it
; does NOT represent real allocated memory in any way.
;-----------------------------------------------
init_VM_pool move.l vm_bytes,d0 ;get requested VM size (1Mb multiples)
moveq #PAGE_SHIFT,d1 ;turn into # of pages
lsr.l d1,d0
move.l d0,vm_total_pages ;initialize available VM pages
move.l d0,vm_pages ;remember maximum total (constant)
lsr.l #3,d0 ;= # of bytes for bitmap
move.l d0,bitmap_size ;remember size of bitmap for dealloc
move.l #MEMF_CLEAR,d1
move.l 4.w,a6
EXEC AllocMem
move.l d0,vm_bitmap
rts ;return EQ/NE (FAIL/OK)
;-----------------------------------------------
; Attempt to allocate a VM block of a given size.
;
; D0 = VM block size. (will be rounded up to an integral # of pages)
;-----------------------------------------------
alloc_VM movem.l d2-d7/a2-a6,-(sp) ;protect non-scratch
add.l #PAGE_SIZE-1,d0 ;round up to int # of pages
and.l #~(PAGE_SIZE-1),d0
move.l d0,d7 ;remember rounded blocksize
moveq #PAGE_SHIFT,d1
lsr.l d1,d0 ;calc # of pages
cmp.l vm_total_pages,d0 ;if request is more than available
bhi too_big ;VM : sod off.
move.l vm_bitmap,a0 ;-> cached bitmap ptr
move.l d0,d4 ;# of free pages (=bits) to find
moveq #0,d5 ;starting looking from bit #0
move.l vm_pages,d6
sub.l d4,d6 ;max number of "window" positions
move.l #VM_START,a5 ;scanning from start of VM...
;---------------
check_window move.l d5,d0 ;is first window bit 0 ?
bsr check_bit
bne slide_along
move.l d5,d2 ;yes, check entire window
move.l d4,d3 ;(pass starting bitno & window length)
bsr check_empty_fld ;enough contiguous 0s in a row ?
beq alloc_window ;yep, go set them and return VMPTR
slide_along add.l #1,d5 ;no, check next possible window
add.l #PAGE_SIZE,a5 ;in parallel track VMPTR
sub.l #1,d6 ;more pages available ?
bne check_window
too_big moveq #0,d0 ;unable to allocate VM block!
bra done_alloc
;---------------
alloc_window move.l a5,a1 ;a1-> VM start address (& 7fffffff)
sub.l #VM_START,a1
move.l d7,d0
bsr alloc_pages ;go set bits
move.l a5,d0 ;return VM address
done_alloc movem.l (sp)+,d2-d7/a2-a6 ;restore non-scratch registers
rts
;-----------------------------------------------
; Check if VM bitmap bitstring starting at bit position P has N clr bits
; A0 -> bitmap
; D2 = starting bit #
; D3 = # of bits that we want
; RETURNS : EQ if bitfield is long enough USES: D0/D1/D2/D3
;-----------------------------------------------
check_empty_fld sub.w #1,d3 ;-1 DBRA
check_match move.l d2,d0
add.l #1,d2
bsr check_bit
dbne d3,check_match ;**!! 65536 pages limit
rts
;-----------------------------------------------
; D0 = bit # to check in bitmap USES: D0/D1
; A0 -> bitmap
; RETURNS: EQ/NE for 0/1 bit
;-----------------------------------------------
check_bit moveq #7,d1
and.w d0,d1
not.w d1 ;d1= bit # in byte
lsr.l #3,d0 ;d0= byte #
btst d1,0(a0,d0.l)
rts
;-----------------------------------------------
; Free a previously allocated VM block.
; A1 -> VM address
; D0 = block size
;-----------------------------------------------
free_VM movem.l d2-d7/a2-a6,-(SP)
sub.l #VM_START,a1 ;any ptr has to be a VM ptr !!
bcc its_a_VM_ptr
moveq #-1,d0 ;if top bit was not set: return error
bra bad_VMPTR
its_a_VM_ptr add.l #PAGE_SIZE-1,d0 ;round up to int # of pages
and.l #~(PAGE_SIZE-1),d0
moveq #PAGE_SHIFT,d1
lsr.l d1,d0 ;# of page bits to clear
add.l d0,vm_total_pages ;track how much gets freed
move.l a1,d1 ;-> VM address
moveq #PAGE_SHIFT,d2
lsr.l d2,d1 ;first page bit # to clear
move.l D_tables,a3 ;-> base of all D-level page descr
lea 0(a3,d1.l*4),a3 ;-> 1st page descr of block to free
moveq #7,d2
and.w d1,d2
eor.w #7,d2 ;bit # in first byte
lsr.l #3,d1 ;= bitmap byte offset
move.l vm_bitmap,a2 ;-> base of bitmap
add.l d1,a2 ;-> byte in bitmap
move.l 4.w,a6 ;in case we need to FreeMem() pages
move.l #SHORT_INVALID,d7 ;cached INVALID descriptor
move.l #PTR_MASK,d6 ;mask to isolate ptr from descriptor
move.l d0,d3 ;# of pages to free
;---------------
dealloc_pages move.l (a3),d4 ;get current page descriptor
bclr #0,d4
beq was_invalid ;if descriptor contains real page ptr
and.l d6,d4 ;mask out any non-ptr bits
move.l d4,a1 ;-> RAM
move.l #PAGE_SIZE,d0 ;free this RAM page as we go
EXEC FreeMem ;**!! RECURSION
was_invalid move.l d7,(a3)+ ;invalidate descriptor in MMU table
bclr d2,(a2) ;deallocate page bit in bitmap
sub.w #1,d2 ;dec bitnumber
bpl in_byte ;if wraps from 0 to -1
moveq #7,d2 ;reset bit # to 7
add.l #1,a2 ;and goto next byte in bitmap
in_byte sub.l #1,d3 ;more pages to free ?
bne dealloc_pages
;---------------
moveq #0,d0 ;return OK.
bad_VMPTR movem.l (sp)+,d2-d7/a2-a6
rts
;-----------------------------------------------
; Allocate a page range that we've identified as being free.
; A1 -> VM address (without bit31 set)
; D0 = block size
;-----------------------------------------------
alloc_pages moveq #PAGE_SHIFT,d1
lsr.l d1,d0 ;# of page bits to clear
sub.l d0,vm_total_pages ;track how many free pages left
move.l a1,d1
moveq #PAGE_SHIFT,d2
lsr.l d2,d1 ;first page bit # to clear
moveq #7,d2
and.w d1,d2
eor.w #7,d2 ;first bit in byte
lsr.l #3,d1 ;= bitmap byte offset
move.l vm_bitmap,a0
add.l d1,a0 ;-> byte in bitmap
set_page_bits bset d2,(a0) ;allocate page
sub.w #1,d2 ;dec bitnumber
bpl in_byte2 ;if wraps from 0 to -1
moveq #7,d2 ;reset bit # to 7
add.l #1,a0 ;and goto next byte in bitmap
in_byte2 sub.l #1,d0 ;more bits to set ?
bne set_page_bits
rts
;-----------------------------------------------
; Modify system structures to handle new use of VM pointers
; by a single Process.
;-----------------------------------------------
install_patches bsr gen_VM_tree ;gen MMU translation tree for VM LAs
bsr use_new_tree ;change MMU to use new, bigger tree
st hard_patched ;MMU has been reconfigured !!
move.l 4.w,a6 ;using Exec...
FORBID
bsr patch_Exec ;patch into AllocMem(), FreeMem()
bsr patch_DOS ;patch into Read(), Write()
move.l $8,normal_bus_err+2 ;our handler passes exception on if
move.l #bus_err_handler,$8 ;can't handle it
move.l 4.w,a6 ;using Exec...
PERMIT
st soft_patched ;SYSTEM HAS BEEN MODIFIED !!
rts
;-----------------------------------------------
; Unpatch all VM handling stuff from system.
; (If patches ever installed)
;-----------------------------------------------
unpatch_all move.l 4.w,a6 ;using Exec...
tst.b soft_patched ;did we patch it all up ?
beq no_soft_patches
FORBID ;stop others from accessing system in inconsistent state.
move.l a6,a1
lea _LVOAllocMem,a0 ;library entry number
move.l normal_alloc+2,d0 ;restore usual vector
EXEC SetFunction
move.l a6,a1
lea _LVOFreeMem,a0
move.l normal_free+2,d0
EXEC SetFunction
move.l DOS_LIB_PTR,a1
lea _LVORead,a0
move.l normal_Read+2,d0
EXEC SetFunction
move.l DOS_LIB_PTR,a1
lea _LVOWrite,a0
move.l normal_Write+2,d0
EXEC SetFunction
move.l normal_bus_err+2,$8 ;restore std Exec exception vector
move.l 4.w,a6
PERMIT ;OK: everything's clean again.
;---------------
no_soft_patches tst.b hard_patched ;is VM MMU table active ?
req
DISABLE
lea reset_regs,a5 ;MMU changes in supervisor mode
EXEC Supervisor ;do it..
ENABLE
rts
;---------------------------------------
; Reset MMU registers to their original values before APPVM.
;---------------------------------------
reset_regs lea tc_long,a0 ;-> room to construct TC register
clr.l (a0)
pmove.l (a0),TC ;disable MMU
lea old_crp_room,a0
lea old_tc,a1
pmove.d (a0),CRP ;restore original CRP pointer
pmove.l (a1),TC ;re-enable MMU as it was originally
rte
;-----------------------------------------------
; Modify the Exec AllocMem() and FreeMem() calls to handle MEMF_VM.
;-----------------------------------------------
patch_Exec move.l 4.w,a6
move.l a6,a1 ;modify exec.library itself
lea _LVOAllocMem,a0 ;library entry number
move.l #new_AllocMem,d0 ;-> new routine for this entry
EXEC SetFunction ;change library and
move.l d0,normal_alloc+2 ;copy usual vector into my jump
move.l a6,a1
lea _LVOFreeMem,a0
move.l #new_FreeMem,d0
EXEC SetFunction ;same for FreeMem
move.l d0,normal_free+2
rts
;-----------------------------------------------
; Modify the DOS Read() and Write() calls to handle reads and writes from/to VM.
;-----------------------------------------------
patch_DOS move.l DOS_LIB_PTR,a1 ;modify dos.library itself
lea _LVORead,a0
move.l #new_Read,d0
EXEC SetFunction ;install new Read
move.l d0,normal_Read+2
move.l DOS_LIB_PTR,a1
lea _LVOWrite,a0
move.l #new_Write,d0
EXEC SetFunction ;install new AllocMem
move.l d0,normal_Write+2
rts
;-----------------------------------------------
; Some **!! SELF-MODIFIED code follows here ...
normal_alloc jmp $F80000 ;execute standard AllocMem()
normal_free jmp $F80000 ;execute standard FreeMem()
normal_Read jmp $F80000 ;execute standard Read()
normal_Write jmp $F80000 ;execute standard Write()
;-----------------------------------------------
; This is our new AllocMem() function.
; If the MEMF_VM bit is clear, then just execute normal AllocMem().
;
; First of all we try to allocate a physical RAM page because the exception handler
; assumes that there is always at least one mapped page available to steal if
; physical RAM levels are low.
; If this succeeds then we check if we've got enough VM left for this request.
; If we have then the page gets mapped otherwise the page gets freed again.
;
; D0 = VM blocksize
; D1 = MEMF_VM
;-----------------------------------------------
new_AllocMem tst.l d1 ;btst #MEMB_VM,D1
bpl normal_alloc ;if Process wants some VM,
tst.l client_Process ;check if we should know this Process.
bne check_client ;if 1st time call, then
move.l ThisTask(a6),client_Process ;have this Task as our client
clr.l client_usage ;initialize its VM usage counter
bra get_vm
check_client move.l client_Process,a0 ;get current client addr
cmp.l ThisTask(a6),a0 ;"Are you the same one, Oh Task ?"
bne bad_client
;---------------
get_vm movem.l d7/a2-a3,-(sp) ;we're a system routine now !!
move.l d0,request_size ;remember size of requested block
move.l #PAGE_SIZE,d7 ;get RAM aligned to a page boundary
move.l #PAGE_SIZE,d0 ;size of allocation
bsr alloc_aligned ;get fresh page
move.l d0,d7 ;store addr in parm for enable_page
beq no_initial_page ;if succeeded,
move.l request_size,d0
bsr alloc_VM ;handle VM allocation request
move.l d0,naughty_vm_ptr ;if VM allocation fails, then
bne got_vm
move.l d7,a1 ;free RAM page and fail totally
move.l #PAGE_SIZE,d0
EXEC FreeMem
bra no_initial_page
;---------------
got_vm move.l d0,a2 ;-> VM addr at which
move.l d7,a3 ;RAM page should appear.
bsr load_page ;fill page from copy on swapdevice
bsr enable_page ;and change MMU table to enable page
move.l 4.w,a6 ;restore EXEC ptr in A6
move.l request_size,d0 ;track client's VM usage so we can
add.l d0,client_usage ;free client when freeing all.
move.l naughty_vm_ptr,d0 ;return VM ptr
movem.l (sp)+,d7/a2-a3
rts
;---------------
no_initial_page movem.l (sp)+,d7/a2-a3 ;restore non-scratch register
bad_client moveq #0,d0 ;sorry, VM used by other Process
rts
;-----------------------------------------------
; This is our new FreeMem() function.
; If the address of the returned block is not a VM address, then just
; execute normal FreeMem().
;
; D0 = VM blocksize
; A1 -> VM Block
;-----------------------------------------------
new_FreeMem move.l a1,test_ptr ;test if ptr is a VMPTR
bpl normal_free ;yes,
tst.l client_Process ;Process HAS to be our client.
beq VM_FreeMem_err ;impossible not to know him.
move.l client_Process,a0 ;get current client addr
cmp.l ThisTask(a6),a0 ;"Are you the same one, Oh Task ?"
bne VM_FreeMem_err
sub.l d0,client_usage ;when client FreeVM()s all
bne holding_VM ;open door to new client
clr.l client_Process ;client has no more VM: goodbye!
holding_VM bsr free_VM
VM_FreeMem_err rts
;-----------------------------------------------
; This is our new AmigaDOS Read() function.
; If the address of the buffer is not a VM address, then just
; execute normal Read().
;
; D1 = filehandle
; D2 = buffer (possibly VM ptr)
; D3 = size of buffer to fill
;-----------------------------------------------
new_Read tst.l d2 ;test if ptr is a VMPTR
bpl normal_Read ;yes,
move.l client_Process,a0 ;get current client addr (even if NULL)
move.l 4.w,a1
cmp.l ThisTask(a1),a0 ;"Are you the same one, Oh Task ?"
bne alien_uses_VM
movem.l d2-d7/a2-a6,-(sp) ;protect all non-scratch regs !
move.l DOS_LIB_PTR,a6 ;in case Task is naughty !
move.l d1,a5 ;filehandle
move.l d2,d4 ;VM destination
move.l d3,d5 ;# of bytes to transfer
bsr VM_READ
move.l d7,d0 ;return # of bytes successfully read
movem.l (sp)+,d2-d7/a2-a6
rts
alien_uses_VM moveq #0,d0 ;no bytes read: ERROR.
rts
;-----------------------------------------------
; This is our new AmigaDOS Write() function.
; If the address of the buffer is not a VM address, then just
; execute normal Write().
;
; D1 = filehandle
; D2 = buffer (possibly VM ptr)
; D3 = size of buffer to save
;-----------------------------------------------
new_Write tst.l d2 ;test if ptr is a VMPTR
bpl normal_Write ;yes,
move.l client_Process,a0 ;get current client addr
move.l 4.w,a1
cmp.l ThisTask(a1),a0 ;"Are you the same one, Oh Task ?"
bne alien_uses_VM
movem.l d2-d7/a2-a6,-(sp) ;protect all non-scratch regs !
move.l DOS_LIB_PTR,a6 ;in case Task is naughty !
move.l d1,a5 ;filehandle
move.l d2,d4 ;VM destination
move.l d3,d5 ;# of bytes to transfer
bsr VM_WRITE
movem.l (sp)+,d2-d7/a2-a6
move.l d3,d0 ;return # of bytes successfully write
rts
;-----------------------------------------------------------------------------------
; Read N bytes from any file into VM.
; Parts of the read going to VM which isn't currently mapped are transferred
; straight to the swapfile.
; Worst case: No mapped VM exists and we have to do a file_to_file copy.
; Best case: All VM is mapped to RAM and we just fill in all these pages.
;
; A5 -> filehandle
; D4 = VM destination
; D5 = bytes to read
; A6 = DOS_LIB_PTR
;
; RETURN: bytes read in D7
;-----------------------------------------------------------------------------------
VM_READ moveq #0,d7 ;0 bytes read so far..
bclr #31,d4 ;VMPTR to byte offset
move.l d4,d0 ; = byte offset
moveq #PAGE_SHIFT,d1
lsr.l d1,d0 ; = starting page #
move.l D_tables,a4 ;-> base of D-table page descr.
lea 0(a4,d0.l*4),a4 ;-> first page descr of destination
move.l #PAGE_SIZE-1,d0 ;is there an un-aligned portion
and.l d4,d0 ;to read at all ?
beq no_head
;---------------
move.l #PAGE_SIZE,d1
sub.l d0,d1 ;max # of head bytes to read
cmp.l d5,d1 ;is # of bytes smaller than this ?
bcs head_num_ok ;yes,
move.l d5,d1 ;then read that few instead
head_num_ok sub.l d1,d5 ;decr total to read left
move.l (a4)+,d2 ;get page descriptor
bmi external_page0
and.l #PTR_MASK,d2 ;turn into valid machine ptr
add.l d0,d2 ;point to correct read spot
move.l d1,d3 ;Read() N bytes
move.l a5,d1 ;from user file
DOS Read ;straight into physical RAM
add.l d0,d7 ;track bytes successfully read
cmp.l d3,d0 ;if we read less than requested, then
beq align_offset ;we've reached EOF, so exit here
rts
;-----
external_page0 move.l a5,a2 ;from user file
move.l swap_fhandle,a3 ;to swapfile
move.l d4,d2 ;at VM offset
move.l d1,d3 ;copy N bytes
bsr file_to_file
add.l d0,d7
cmp.l d3,d0
beq align_offset
rts
align_offset add.l #PAGE_SIZE-1,d4
and.l #~(PAGE_SIZE-1),d4 ;align VM offset to page boundary
;---------------
no_head tst.l d5 ;any more bytes to read ?
beq VM_read_done
move.l #~(PAGE_SIZE-1),d6 ;any integral # of pages to read ?
and.l d5,d6
beq no_body
moveq #PAGE_SHIFT,d1
lsr.l d1,d6 ;# of whole pages to "load"
read_pages move.l (a4)+,d2 ;get page descriptor
bmi external_page1 ;if page is mapped,
;---------------
and.l #PTR_MASK,d2 ;turn descr into valid machine ptr
move.l #PAGE_SIZE,d3 ;Read() N bytes
move.l a5,d1 ;from user file
DOS Read ;straight into physical RAM
add.l d0,d7 ;track bytes successfully read
cmp.l d3,d0 ;if we read less than requested, then
beq rd_next_page ;we've reached EOF, so exit here
rts
external_page1 move.l a5,a2 ;from user file
move.l swap_fhandle,a3 ;to swapfile
move.l d4,d2 ;at VM offset
move.l #PAGE_SIZE,d3 ;copy exactly one page
bsr file_to_file
add.l d0,d7
cmp.l d3,d0
beq rd_next_page
rts
rd_next_page add.l #PAGE_SIZE,d4 ;keep track of VM read offset
sub.l #PAGE_SIZE,d5 ;track how much left to read
sub.l #1,d6 ;more pages to read ?
bne read_pages
;---------------
no_body tst.l d5 ;any more bytes to read ?
beq VM_read_done
move.l (a4)+,d2 ;get page descriptor
bmi external_page2
and.l #PTR_MASK,d2 ;turn descr into valid machine ptr
move.l d5,d3 ;Read() last N bytes
move.l a5,d1 ;from user file
DOS Read ;straight into physical RAM
add.l d0,d7 ;track bytes successfully read
rts
external_page2 move.l a5,a2 ;from user file
move.l swap_fhandle,a3 ;to swapfile
move.l d4,d2 ;at VM offset
move.l d5,d3 ;copy remaining bytes
bsr file_to_file
add.l d0,d7
VM_read_done rts
;-----------------------------------------------------------------------------------
; Write N bytes from VM to any file.
; Parts of the write from VM which aren't currently mapped are treated as
; no-ops (the page/bytes should already be on the disk !).
; Best case: No mapped VM exists, so we don't do anything.
; Worse case: All VM is mapped to RAM and we have to write all these pages.
;
; A5 -> filehandle
; D4 = VM source
; D5 = bytes to write
; A6 = DOS_LIB_PTR
;-----------------------------------------------------------------------------------
VM_WRITE: bclr #31,d4 ;VMPTR to byte offset
move.l d4,d7 ; = byte offset
moveq #PAGE_SHIFT,d0
lsr.l d0,d7 ; = starting page #
move.l D_tables,a4 ;-> base of D-table page descr.
lea 0(a4,d7.l*4),a4 ;-> first page descr of destination
move.l #PAGE_SIZE-1,d0 ;is there an un-aligned portion
and.l d4,d0 ;to read at all ?
beq no_head_wr
;---------------
move.l #PAGE_SIZE,d1
sub.l d0,d1 ;max # of head bytes to read
cmp.l d5,d1 ;is # of bytes smaller than this ?
bcs head_num_ok2 ;yes,
move.l d5,d1 ;then read that few instead
head_num_ok2 sub.l d1,d5 ;decr total to read left
move.l (a4)+,d2 ;get page descriptor
bmi align_offset2 ;only if page is valid
and.l #PTR_MASK,d2 ;turn into valid machine ptr
add.l d0,d2 ;point to correct read spot
move.l d1,d3 ;Write() N bytes
move.l a5,d1 ;to user file
DOS Write ;straight from physical RAM
;-----
align_offset2 add.l #PAGE_SIZE-1,d4
and.l #~(PAGE_SIZE-1),d4 ;align VM offset to page boundary
;---------------
no_head_wr tst.l d5 ;any more bytes to write ?
beq VM_write_done
move.l #~(PAGE_SIZE-1),d6 ;any integral # of pages to write ?
and.l d5,d6
beq no_body2
moveq #PAGE_SHIFT,d1
lsr.l d1,d6 ;# of whole pages to "load"
write_pages move.l (a4)+,d2 ;get page descriptor
bmi wr_next_page ;only if page is mapped,
;---------------
and.l #PTR_MASK,d2 ;turn descr into valid machine ptr
move.l #PAGE_SIZE,d3 ;Write() N bytes
move.l a5,d1 ;to user file
DOS Write ;straight from physical RAM
wr_next_page add.l #PAGE_SIZE,d4 ;keep track of VM read offset
sub.l #PAGE_SIZE,d5 ;track how much left to read
sub.l #1,d6 ;more pages to write ?
bne write_pages
;---------------
no_body2 tst.l d5 ;any more bytes to read ?
beq VM_write_done
move.l (a4)+,d2 ;get page descriptor
bmi VM_write_done ;only if page is mapped,
and.l #PTR_MASK,d2 ;turn descr into valid machine ptr
move.l d5,d3 ;Read() last N bytes
move.l a5,d1 ;from user file
DOS Write ;straight into physical RAM
VM_write_done rts
;-----------------------------------------------------------------------------------
; Copy N bytes from src file to another at a specific dest position.
;
; A2 -> Source FileHandle (fh1) (file ptr position IMPLICIT)
; A3 -> Dest FileHandle (fh2) (file ptr position EXPLICIT)
;
; D2 = Position to Seek to in Destination File
; D3 = total # of bytes to copy
;
; INTERNALLY: D7 = original buff size
; D4 -> chunk buffer
; D5 = chunk buffer size
;
; RETURN: D0 = number of bytes really transferred
;-----------------------------------------------------------------------------------
file_to_file movem.l d2-d7/a2-a6,-(sp) ;protect non-scratch registers
move.l d2,d0 ;check that copy operation
add.l d3,d0 ;fits entirely inside swapfile!
cmp.l vm_bytes,d0 ;**!! SHOULD NEVER OCCUR
bhi copy_impossible ;yes, it won't overflow swapfile so
;---------------
move.l 4.w,a6 ;using Exec..
move.l d3,d7 ;remember original size
bra try_largest ;try to get largest poss. copy buffer
;---------------
buf_too_big lsr.l #1,d3 ;attempt block half as big then...
try_largest move.l d3,d0 ;attempt buffer of this size
move.l #MEMF_PUBLIC,d1 ;FAST RAM please ! (if possible)
EXEC AllocMem
move.l d0,d4 ;did allocation succeed ?
beq buf_too_big ;OK we've got a block of size N
;---------------
move.l d3,d5 ;remember chunk size
moveq #0,d6 ;no bytes transferred so far.
move.l DOS_LIB_PTR,a6 ;using DOS...
move.l a3,d1 ;set fileptr of destination file
; ; ;D2 = original argument !
move.l #OFFSET_BEGINNING,d3
DOS Seek ;dest fileptr to start of copy pos
move.l d4,d2 ;set TO/FROM buffer ptr (constant)
move.l d5,d3 ;set transfer size (constant)
;---------------
copy_chunk move.l a2,d1 ;Read() a chunk of huge block into
DOS Read ;copy work buffer
add.l d0,d6 ;track # of bytes transferred
move.l a3,d1 ;Write() a chunk of huge block out
move.l d0,d3 ;or less if premature EOF encountered
beq ff_transf_done
DOS Write ;to destination file
sub.l d5,d7 ;track howmany bytes copied so far
cmp.l d5,d7 ;can we do another chunk copy ?
bhi copy_chunk
;---------------
move.l d7,d3
beq perfect_fit ;if any extra bytes to do,
move.l a2,d1 ;copy left overs too..
DOS Read ;read in MOD chunksize bytes
add.l d0,d6 ;track how many bytes transferred
move.l a3,d1
move.l d0,d3
DOS Write ;and copy them out again
;---------------
perfect_fit move.l 4.w,a6 ;return temporary copy buffer!
move.l d4,a1
move.l d5,d0
EXEC FreeMem
ff_transf_done move.l d6,d0 ;return # of bytes actually copied
movem.l (sp)+,d2-d7/a2-a6
rts
copy_impossible moveq #0,d0 ;return FAIL. Not enough VM.
movem.l (sp)+,d2-d7/a2-a6
rts
;-----------------------------------------------
; Open libraries, console handle, init defaults
;-----------------------------------------------
init_all clr.l client_Process ;no Task using our VM yet..
clr.l ager ;reset descriptor ageing index
sf soft_patched ;don't touch system when "unpatching"!
sf hard_patched ;don't stuff invalid values in MMU
move.l 4.w,a6 ;using Exec...
lea dosname,a1
moveq #0,d0
EXEC OpenLibrary ;open DOS
move.l d0,DOS_LIB_PTR
beq END_APPVM
move.l d0,a6
DOS Output ;get stdout handle
move.l d0,stdout
rts
;-----------------------------------------------
; Free libraries, handles...
;-----------------------------------------------
close_all move.l DOS_LIB_PTR,d0 ;did we ever get DOS ?
beq no_dos ;yes,
move.l d0,a6 ;then close swapfile first
move.l swap_fhandle,d1 ;if we ever got that file
beq no_swaphandle
DOS Close
no_swaphandle move.l a6,a1 ;then close library itself
move.l 4.w,a6
EXEC CloseLibrary
;---------------
no_dos
dummy rts
;-----------------------------------------------
; D0 = size of block to get
; D7 = block should be aligned to ... byte boundary (powers of two!)
;
; This is done by first attempting to allocate a block that is guaranteed
; to CONTAIN a block with the correct alignment, noting that base address,
; then doing an AllocAbs() of the aligned block inside that block.
;-----------------------------------------------
alloc_aligned movem.l d2-d7/a2-a6,-(sp)
move.l 4.w,a6 ;using Exec
FORBID ;disable allocs between Free & AllocAbs
move.l d0,d6 ;remember original requested size
add.l d7,d0 ;incr. blocksize to get a size which
move.l d0,d3 ;guarantees a contained aligned base
move.l #MEMF_PUBLIC,d1 ;attempt to get this block which
EXEC AllocMem ;encloses the block we really want.
move.l d0,d2
beq fail_alloc
;---------------
move.l d3,d0 ;ok, now release this "test" block
move.l d2,a1
EXEC FreeMem ;and from its base address
sub.l #1,d7 ;calc address of aligned block
add.l d7,d2
not.l d7
and.l d7,d2 ;-> aligned ptr
move.l d2,a1
move.l d6,d0
EXEC AllocAbs ;in theory this call cannot fail !
move.l d0,d2
PERMIT
move.l d2,d0 ;return ptr or NULL
movem.l (sp)+,d2-d7/a2-a6
rts
fail_alloc PERMIT
moveq #0,d0 ;aligned allocation FAILED
movem.l (sp)+,d2-d7/a2-a6
rts
;-----------------------------------------------
include "SRC:UTILS/ReadArgs.s"
include "SRC:MODULES/LIB1/DEC_TO_BIN"
;-----------------------------------------------
; Program Strings, Constants
;-----------------------------------------------
no_table_str dc.b "Couldn't allocate new MMU translation tables!",0
give_example_str dc.b "Example: APPVM 20 SWAPFILE DEVS:swapfile",0
too_much_vm_str dc.b "Kindly request less than 256 Megabytes for VM management.",0
not_enough_vm_str dc.b "Kindly request at least 1 Megabyte.",0
no_VM_init_str dc.b "Not enough memory to initialise VM pool.",0
unable_to_create_str dc.b "Could not create swapfile.",0
disk_full_str dc.b "Not enough room on disk for swapfile.",0
resident_str dc.b "APPVM is already active.",0
need_MMU_str dc.b "APPVM requires an MMU to enable Virtual Memory.",0
dosname DOSNAME
default_swfn dc.b "Work:T/SWAPFILE",0
vm_node_name dc.b "VM_NODE",0
APPVM_template dc.b "VMMEGS/A,SWAPFILE/K",0
VM_ready dc.b LF,"Virtual Memory Manager 0.9 "
dc.b ESC,"[7m" ;HIGHLIGHT ON
dc.b "Ready"
dc.b ESC,"[0m" ;HIGHLIGHT OFF
dc.b LF
dc.b "Written by Laurence Vanhelsuwé © March 1992",0
VM_off dc.b LF,"Virtual Memory Manager no longer active.",LF,0
appVM_version dc.b "$VER: APPVM 0.9 ©LVA (Alpha release) 27/MAR/92",0
dc.b "Copyright material, see user license for details.",0
;-----------------------------------------------
;Program variables, pointers, flags
;-----------------------------------------------
arg_line ds.l 1 ;-> raw arguments
stack_level ds.l 1 ;program launch SP
DOS_LIB_PTR ds.l 1 ;-> opened dos.library
stdout ds.l 1 ;-> console file
swap_fhandle ds.l 1 ;file handle for swapfile
continue_ssp ds.l 1 ;exception handler SSP level (SSP must remain same)
client_Process ds.l 1 ;-> NULL or ONLY Task/Process which is using VM
client_usage ds.l 1 ; # of VM bytes client is using at this moment.
test_ptr ds.l 1 ;a LONG to test ptr in A reg without using regs
naughty_vm_ptr ds.l 1 ;VMPTR that caused bus error exception
request_size ds.l 1 ;for AllocVM() to remember # of bytes
vm_tree ds.l 1 ;-> root of tree (A-level table)
B_table ds.l 1 ;-> one and only VM B-table
C_tables ds.l 1
D_tables ds.l 1 ;-> ptr to INVALID descriptors and valid PAGE descr.
ager ds.l 1 ;index into descriptors
old_crp_room ds.l 2 ;room to store current 64-bit CRP register
old_tc ds.l 1 ;same for TC
new_crp_room ds.l 2 ;room to store future 64-bit CRP register
tc_long ds.l 1
; VM Pool management variables
; ----------------------------
vm_bytes ds.l 1 ;size of VM in bytes (rounded)
vm_total_pages ds.l 1 ;total amount of VM pages remaining
vm_pages ds.l 1 ;total amount of VM pages (constant **!!)
vm_bitmap ds.l 1 ;-> allocated pages bitmap
bitmap_size ds.l 1 ;size of above
pages_mapped ds.l 1 **!!
; ReadArgs() results array
; ------------------------
EVEN
appvm_results
vm_megs ds.l 1
swap_filename ds.l 1
; Flags
; -----
soft_patched ds.b 1 ;tell unpatch_all to undo work..
hard_patched ds.b 1 ;restore old MMU configuration
END